package com.example.statemachine.lock;

import com.example.statemachine.dao.VULockMapper;
import com.example.statemachine.util.BusinessException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author jianwei.wang
 * @version v1.0
 * @date 2019/5/10 11:11
 **/
@Component
@Aspect
@Order
public class VULockAop {

    @Autowired
    private VULockMapper vuLockMapper;

    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    @Pointcut("@annotation(com.example.statemachine.lock.VULock)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        transactionTemplate.setName(point.getSignature().getName()+"_aop_transaction");
        return transactionTemplate.execute(transactionStatus -> {
            dataSourceTransactionManager.setRollbackOnCommitFailure(true);
            dataSourceTransactionManager.setGlobalRollbackOnParticipationFailure(false);
            try {
                int status = vuLockMapper.update("credential", "sid = '1' and version = '2'");
                if (status != 1){
                    throw new BusinessException("数据已变更，请刷新重试");
                }
                return point.proceed();
            }catch (BusinessException b) {
                dataSourceTransactionManager.rollback(transactionStatus);
                throw b;
            } catch (Throwable throwable) {
                dataSourceTransactionManager.rollback(transactionStatus);
                throw new RuntimeException(throwable);
            }finally {
                boolean complete = transactionStatus.isCompleted();
                if (!complete){
                    dataSourceTransactionManager.commit(transactionStatus);
                }
            }
        });
//        return execute(point,transactionStatus);
    }

    private Object execute(ProceedingJoinPoint point, TransactionStatus status) throws Throwable {
        try {
            updateVersion(point);
            return point.proceed(point.getArgs());
        } catch (Exception e) {
            dataSourceTransactionManager.rollback(status);
            throw e;
        }finally {
            if (null != status){
                boolean completed = status.isCompleted();
                if (!completed){
                    dataSourceTransactionManager.commit(status);
                }
            }
        }
    }

    private void updateVersion(ProceedingJoinPoint point) throws IllegalAccessException {
        //获取参数对象
        Object[] args = point.getArgs();
        //获取方法参数
        MethodSignature signature = (MethodSignature) point.getSignature();
        Parameter[] parameters = signature.getMethod().getParameters();
        for (Parameter parameter : parameters) {
            List<Field> fieldList = new ArrayList<>();
            StringBuilder builder = new StringBuilder();
            Class<?> clazz = parameter.getType();

            Object object = Arrays.asList(args).stream().filter(a -> clazz.isAssignableFrom(a.getClass())).findAny().get();
            Class<?> pClazz = clazz.getSuperclass();
            Field[] fields = clazz.getDeclaredFields();
            Field[] pFields = pClazz.getDeclaredFields();
            fieldList.addAll(Arrays.asList(fields));
            fieldList.addAll(Arrays.asList(pFields));
            for (Field field : fields) {
                field.setAccessible(true);
                LockField lockField = field.getAnnotation(LockField.class);
                if (null != lockField) {
                    String name = field.getName();
                    Object value = field.get(object);
                    if (null == value) {
                        throw new BusinessException("必传参数不能为空");
                    }
                    if (!builder.toString().isEmpty()) {
                        builder.append(" and ");
                    }
                    //驼峰转下划线
                    name = name.replaceAll("[A-Z]", "_$0").toLowerCase();
                    builder.append(name).append(" = ").append("'").append(value).append("'");
                }
            }
            if (!builder.toString().isEmpty()){
                //获取表名
                Method method = ((MethodSignature) point.getSignature()).getMethod();
                String table = method.getAnnotation(VULock.class).value().getTableName();
                //更新版本号
                int count = vuLockMapper.update(table, builder.toString());
                if (count == 0) {
                    throw new BusinessException("数据已变更，请刷新重试");
                }
            }
        }
    }
}
